/*
 * $Id: GradientPreviewPanel.java 4082 2011-11-15 18:39:43Z kschaefe $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.jdesktop.swingx.color;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LinearGradientPaint;
import java.awt.MultipleGradientPaint;
import java.awt.Paint;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.util.List;
import java.util.logging.Logger;

import javax.swing.event.MouseInputAdapter;

import org.jdesktop.swingx.JXGradientChooser;
import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.multislider.MultiThumbModel;
import org.jdesktop.swingx.multislider.Thumb;
import org.jdesktop.swingx.util.PaintUtils;

/**
 * <p>
 * <b>Dependency</b>: Because this class relies on LinearGradientPaint and
 * RadialGradientPaint, it requires the optional MultipleGradientPaint.jar
 * </p>
 * 
 * @author joshy
 */
public class GradientPreviewPanel extends JXPanel {
	private Paint checker_texture = null;
	private Point2D start, end;
	public JXGradientChooser picker;
	boolean moving_start = false;
	boolean moving_end = false;
	private boolean radial = false;
	private boolean reversed = false;
	private boolean reflected = false;
	private boolean repeated = false;
	private MultipleGradientPaint gradient;

	public GradientPreviewPanel() {
		start = new Point2D.Float(10, 10);
		end = new Point2D.Float(80, 10);
		checker_texture = PaintUtils.getCheckerPaint();
		MouseInputAdapter ma = new GradientMouseHandler();
		this.addMouseListener(ma);
		this.addMouseMotionListener(ma);
	}

	public void setGradient() {
		repaint();
	}

	public void setGradient(MultipleGradientPaint grad) {
		MultipleGradientPaint old = getGradient();
		if (grad instanceof LinearGradientPaint) {
			LinearGradientPaint paint = (LinearGradientPaint) grad;
			this.start = paint.getStartPoint();
			this.end = paint.getEndPoint();
		} else {
			RadialGradientPaint paint = (RadialGradientPaint) grad;
			this.start = paint.getCenterPoint();
			this.end = new Point2D.Double(start.getX(), start.getY() + paint.getRadius());
		}
		this.gradient = grad;
		firePropertyChange("gradient", old, getGradient());
		repaint();
	}

	public MultipleGradientPaint getGradient() {
		return this.gradient;
	}

	public MultipleGradientPaint calculateGradient() {
		List<Thumb<Color>> stops = getStops();
		int len = stops.size();

		// set up the data for the gradient
		float[] fractions = new float[len];
		Color[] colors = new Color[len];
		int i = 0;
		for (Thumb<Color> thumb : stops) {
			colors[i] = (Color) thumb.getObject();
			fractions[i] = thumb.getPosition();
			i++;
		}

		// get the final gradient
		this.setGradient(calculateGradient(fractions, colors));
		return getGradient();
	}

	private MultiThumbModel model;
	private Logger log = Logger.getLogger(GradientPreviewPanel.class.getName());

	private List<Thumb<Color>> getStops() {
		// calculate the color stops
		return model == null ? null : model.getSortedThumbs();
	}

	public void setMultiThumbModel(MultiThumbModel model) {
		MultiThumbModel old = getMultiThumbModel();
		this.model = model;
		firePropertyChange("multiThumbModel", old, getMultiThumbModel());
	}

	public MultiThumbModel getMultiThumbModel() {
		return this.model;
	}

	@Override
	protected void paintComponent(Graphics g) {
		try {
			Graphics2D g2 = (Graphics2D) g;

			// fill the background with checker first
			g2.setPaint(checker_texture);
			g.fillRect(0, 0, getWidth(), getHeight());

			Paint paint = getGradient();
			// fill the area
			if (paint != null) {
				g2.setPaint(paint);
			} else {
				g2.setPaint(Color.black);
			}

			g.fillRect(0, 0, getWidth(), getHeight());

			drawHandles(g2);
		} catch (Exception ex) {
			log.severe("ex: " + ex);
		}
	}

	private MultipleGradientPaint calculateGradient(final float[] fractions, final Color[] colors) {
		// set up the end points
		Point2D start = this.start;
		Point2D end = this.end;
		if (isReversed()) {
			// if(picker.reversedCheck.isSelected()) {
			start = this.end;
			end = this.start;
		}

		// set up the cycle type
		MultipleGradientPaint.CycleMethod cycle = MultipleGradientPaint.CycleMethod.NO_CYCLE;
		if (isRepeated()) {
			// if(picker.repeatedRadio.isSelected()) {
			cycle = MultipleGradientPaint.CycleMethod.REPEAT;
		}
		if (isReflected()) {
			// if(picker.reflectedRadio.isSelected()) {
			cycle = MultipleGradientPaint.CycleMethod.REFLECT;
		}

		// create the underlying gradient paint
		MultipleGradientPaint paint = null;
		if (isRadial()) { // picker.styleCombo.getSelectedItem().toString().equals("Radial"))
							// {
			paint = new RadialGradientPaint(start, (float) start.distance(end), start, fractions, colors, cycle,
					MultipleGradientPaint.ColorSpaceType.SRGB, null);
		} else {
			paint = new LinearGradientPaint((float) start.getX(), (float) start.getY(), (float) end.getX(), (float) end.getY(),
					fractions, colors, cycle);
		}
		return paint;
	}

	private void drawHandles(final Graphics2D g2) {
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		// draw the points and gradient line
		g2.setColor(Color.black);
		g2.drawOval((int) start.getX() - 5, (int) start.getY() - 5, 10, 10);
		g2.setColor(Color.white);
		g2.drawOval((int) start.getX() - 4, (int) start.getY() - 4, 8, 8);

		g2.setColor(Color.black);
		g2.drawOval((int) end.getX() - 5, (int) end.getY() - 5, 10, 10);
		g2.setColor(Color.white);
		g2.drawOval((int) end.getX() - 4, (int) end.getY() - 4, 8, 8);

		g2.setColor(Color.darkGray);
		g2.drawLine((int) start.getX(), (int) start.getY(), (int) end.getX(), (int) end.getY());
		g2.setColor(Color.gray);
		g2.drawLine((int) start.getX() - 1, (int) start.getY() - 1, (int) end.getX() - 1, (int) end.getY() - 1);
	}

	private class GradientMouseHandler extends MouseInputAdapter {

		@Override
		public void mousePressed(MouseEvent evt) {
			moving_start = false;
			moving_end = false;
			if (evt.getPoint().distance(start) < 5) {
				moving_start = true;
				start = evt.getPoint();
				return;
			}

			if (evt.getPoint().distance(end) < 5) {
				moving_end = true;
				end = evt.getPoint();
				return;
			}

			start = evt.getPoint();
		}

		@Override
		public void mouseDragged(MouseEvent evt) {
			if (moving_start) {
				start = evt.getPoint();
			} else {
				end = evt.getPoint();
			}
			calculateGradient();
			repaint();
		}
	}

	public boolean isRadial() {
		return radial;
	}

	public void setRadial(boolean radial) {
		boolean old = isRadial();
		this.radial = radial;
		firePropertyChange("radial", old, isRadial());
	}

	public boolean isReversed() {
		return reversed;
	}

	public void setReversed(boolean reversed) {
		boolean old = isReversed();
		this.reversed = reversed;
		firePropertyChange("reversed", old, isReversed());
	}

	public boolean isReflected() {
		return reflected;
	}

	public void setReflected(boolean reflected) {
		boolean old = isReflected();
		this.reflected = reflected;
		firePropertyChange("reflected", old, isReflected());
	}

	public boolean isRepeated() {
		return repeated;
	}

	public void setRepeated(boolean repeated) {
		boolean old = isRepeated();
		this.repeated = repeated;
		firePropertyChange("repeated", old, isRepeated());
	}
}
